<?php
/**
 ***********************************************************************************************************************
 * 输出控制类
 * @autor llq
 ***********************************************************************************************************************
 */
namespace artisan;

use mike\zipkin\Core;
use model\parter\emp\emp;

/**
 * Class output
 * @package artisan
 */
class output
{
    /**
     * @var int HTTP 状态码
     */
    private static $http_code = 200;

    /**
     * @var array 错误码
     */
    private static $error_code_map = [];

    /**
     * @var int 错误码
     */
    private static $error_code = 0;

    /**
     * @var string 错误消息
     */
    private static $error_msg = '成功';

    /**
     * @var string 响应内容
     */
    private static $response = '';

    /**
     * @var string 响应数据
     */
    private static $data = '';

    /**
     * @var string 布局
     */
    private static $layout = '';

    /**
     * @var array 布局变量
     */
    private static $layoutData = [];

    /**
     * @var string 模板
     */
    public static $template = '';

    /**
     * @var array 模板变量
     */
    private static $templateData = [];

    /**
     * @var array 闭包函数
     */
    private static $closures = array();

    /**
     * 设置当前请求的布局模板和布局模板变量内容 & 设置false可以关闭布局，直接输出当前模板
     *
     * @param string $layout                   示例：mylayout_dir/mylayout
     * @param array $layoutData                示例：['menu' => 'hello menu']
     */
    public static function setLayout($layout = '', array $layoutData = array())
    {
        self::$layout = $layout;
        self::$layoutData = $layoutData;
    }

    /**
     * 设置当前请求的布局模板变量内容
     *
     * @param array $layoutData
     */
    public static function setLayoutData(array $layoutData = array())
    {
        self::$layoutData = $layoutData;
    }

    /**
     * 设置当前请求的模板 和模板变量内容
     *
     * @param string $template                 示例：mytemplate_dir/mytemplate
     * @param array $templateData              示例：['var' => 'hello world']
     */
    public static function setTemplate($template = '', array $templateData = array())
    {
        self::$template = $template;
        self::$templateData = $templateData;
    }

    /**
     * 设置当前请求的模板变量内容
     *
     * @param array $templateData
     */
    public static function setTemplateData(array $templateData = array())
    {
        self::$templateData = $templateData;
    }

    /**
     * 设置响应内容
     *
     * @param array $data
     */
    public static function setData($data = [])
    {
        self::$templateData = (array)$data;
        self::$data = $data;
    }

    /**
     * 获取响应内容
     *
     * @return string
     */
    public static function getResponse()
    {
        return self::$response;
    }

    /**
     * 获取响应内容
     *
     * @return string
     */
    public static function setResponse($response,$format=true)
    {
        return self::$response=$format?self::format($response):$response;
    }

    /**
     * 输出最终结果
     *
     * @param $content
     */
    public static function flush($content = '')
    {
        ob_start();
        self::setHttpHeader();
        // 没有用户定义的延迟执行事件 直接输出内容关闭进程
        if(empty(self::$closures))  {
            echo self::$response = self::format($content);
            ob_end_flush();
            exit;
        }

        // PHP-FPM模式运行
        if('fpm-fcgi' == PHP_SAPI) {
            echo self::$response = self::format($content);
            ob_end_flush();
            fastcgi_finish_request();
        } else {
            // 主动关闭客户端连接
            header("Connection: close\r\n");
            echo self::$response = self::format($content);
            // 获取缓冲区内容长度
            $size = ob_get_length();
            header("Content-Length: ". $size . "\r\n");
            // 输出内容
            ob_end_flush();
            // 强制输出
            ob_flush();
            // 再次输出
            flush();
        }

    }

    /**
     * 获取模板内容
     *
     * @param string $template
     * @param array $templateData
     * @return string
     */
    public static function getTemplateContent($__template__ = '', array $__templateData__ = array())
    {
        $__template__ = empty($__template__) ? self::$template : (string) $__template__;
        $__template__ = strpos($__template__, '.php') ? $__template__ : $__template__ . '.php';
        $file = APPPATH . 'view/' . preg_replace('/^\/?view\//', '', $__template__);
        if(file_exists($file)) {
            ob_start();
            extract($__templateData__ ? $__templateData__ : self::$templateData);
            require $file;
            return ob_get_clean();
        }
        return '';
    }

    /**
     * 格式化输出内容
     *
     * @param string $content
     * @return string
     */
    private static function format($content = '')
    {
        self::$error_code_map = self::$error_code_map ?: config::get('code');
        switch(outputFormat()) {
            case 'text/json':
                if(is_array($content) && isset($content['set_sign']) && $content['set_sign']  === true && isset($content['sign'])){
                    return json_encode(array(
                        'code' => self::$error_code,
                        // 优先接收用户最后传入的msg
                        'sign' => $content['sign'],
                        'msg' => self::$error_msg ? preg_replace('/[\n]/', ' ', strip_tags(self::$error_msg)) : (isset(self::$error_code_map[self::$error_code]) ? self::$error_code_map[self::$error_code] : ''),
                        'data' => $content['data']
                    ),JSON_UNESCAPED_UNICODE);
                }

                header("Content-type:application/json");
                // 格式化状态码
                return json_encode(array(
                    'code' => self::$error_code,
                    // 优先接收用户最后传入的msg
                    'msg' => self::$error_msg ? preg_replace('/[\n]/', ' ', strip_tags(self::$error_msg)) : (isset(self::$error_code_map[self::$error_code]) ? self::$error_code_map[self::$error_code] : ''),
                    'data' => self::$data ?: $content
                ), JSON_UNESCAPED_UNICODE);

                break;
            case 'text/xml':
                return '<?xml version="1.0" encoding="UTF-8"?>' . format::array2xml(array(
                    'code' => self::$error_code,
                    // 优先接收用户最后传入的msg
                    'msg' => self::$error_msg ? preg_replace('/[\n]/', ' ', strip_tags(self::$error_msg)) : (isset(self::$error_code_map[self::$error_code]) ? self::$error_code_map[self::$error_code] : ''),
                    'data' =>  self::$data ?: $content
                ));
                break;
            case 'text/html':
                // 布局变量优先级高于模板变量（就是说，布局变量会覆盖模板变量）
                self::$layoutData = array_merge(self::$templateData, self::$layoutData);
                // 加载布局内容
                $layout = (self::$layout || self::$layout === false) ? self::$layout : globalConfig('view.layout');
                // 如果用户关闭了布局模板 直接输出当前模板内容
                return ($layout === false) ? self::getTemplateContent(self::$template, self::$templateData) : self::getTemplateContent($layout, self::$layoutData);
                break;
            default:
                return (string)$content;
        }
    }

    /**
     * 动态设置输出格式
     *
     * @param $format
     */
    public static function setOutputFormat($format)
    {
        artisan::$output_format = (string)$format;
    }

    /**
     * 动态设置错误状态码
     *
     * @param $error_code
     * @param string $error_msg
     */
    public static function setErrorCode($error_code = 0)
    {
        self::$error_code = abs((int)$error_code);
    }

    /**
     * 动态设置错误消息
     *
     * @param $error_code
     * @param string $error_msg
     */
    public static function setErrorMsg($error_msg = '')
    {
        self::$error_msg = (string)$error_msg;
    }

    /**
     * 抛出错误 兼容Http Code
     *
     * @param $error_code
     * @param string $error_msg
     * @param mixed $data
     */
    public static function raise($error_code, $error_msg = '', $data = [])
    {
        self::$error_code = (int)$error_code;
        self::$error_msg = (string)$error_msg;
        $data = is_array($data) && empty($data) ? (object)[] : $data;
        ob_start();
        self::setHttpHeader();
        echo self::$response = (outputFormat() !== 'text/html') ? self::format($data) : $error_msg;
        ob_end_flush();
        exit;
    }

    /**
     * 请求直接重定向
     *
     * @param string $url
     * @param int $status
     */
    public static function redirect($url = '/', $status = 302)
    {
        header('HTTP/1.1' . ' ' . self::$statuses[$status]);
        header("Location: " . $url);
        exit;
    }

    /**
     * 注册请求结束时执行事件
     *
     * @param callable $event
     */
    public static function later(\Closure $event)
    {
        self::$closures[] = $event;
    }

    /**
     * 异步后台执行方法
     */
    public static function finish()
    {
        if(!empty(self::$closures)) {
            foreach(self::$closures as $closure) {
                call_user_func($closure);
            }
            exit;
        }
    }

    /**
     * 设置HTTP响应头
     */
    public static function setHttpHeader()
    {
        if(!headers_sent()) {
            self::$http_code = array_key_exists(self::$error_code, self::$statuses) ? self::$error_code : self::$http_code;
            header('HTTP/1.1 ' . self::getHttpCodeString(self::$http_code));
            header('Status: ' . self::getHttpCodeString(self::$http_code));
            // 强制使用utf-8
            header("Content-type: text/html; charset=utf-8");
        }

        Core::getInstance()->afterRequest();
    }

    /**
     * 设置HTTP状态码
     */
    public static function setHttpCode($http_code = 200)
    {
        self::$http_code = $http_code;
    }

    /**
     * 获取http code 字符串
     *
     * @param int $http_code
     * @return mixed
     */
    public static function getHttpCodeString($http_code = 200)
    {
        $http_code = $http_code ?: 200;
        return isset(self::$statuses[$http_code]) ? self::$statuses[$http_code] : self::$statuses[200];
    }

    /**
     * @var array
     */
    public static $statuses = [
        //Informational 1xx
        100 => '100 Continue',
        101 => '101 Switching Protocols',
        //Successful 2xx
        200 => '200 OK',
        201 => '201 Created',
        202 => '202 Accepted',
        203 => '203 Non-Authoritative Information',
        204 => '204 No Content',
        205 => '205 Reset Content',
        206 => '206 Partial Content',
        //Redirection 3xx
        300 => '300 Multiple Choices',
        301 => '301 Moved Permanently',
        302 => '302 Moved Temporarily',
        303 => '303 See Other',
        304 => '304 Not Modified',
        305 => '305 Use Proxy',
        306 => '306 (Unused)',
        307 => '307 Temporary Redirect',
        //Client Error 4xx
        400 => '400 Bad Request',
        401 => '401 Unauthorized',
        402 => '402 Payment Required',
        403 => '403 Forbidden',
        404 => '404 Not Found',
        405 => '405 Method Not Allowed',
        406 => '406 Not Acceptable',
        407 => '407 Proxy Authentication Required',
        408 => '408 Request Timeout',
        409 => '409 Conflict',
        410 => '410 Gone',
        411 => '411 Length Required',
        412 => '412 Precondition Failed',
        413 => '413 Request Entity Too Large',
        414 => '414 Request-URI Too Long',
        415 => '415 Unsupported Media Type',
        416 => '416 Requested Range Not Satisfiable',
        417 => '417 Expectation Failed',
        418 => '418 I\'m a teapot',
        422 => '422 Unprocessable Entity',
        423 => '423 Locked',
        //Server Error 5xx
        500 => '500 Internal Server Error',
        501 => '501 Not Implemented',
        502 => '502 Bad Gateway',
        503 => '503 Service Unavailable',
        504 => '504 Gateway Timeout',
        505 => '505 HTTP Version Not Supported'
    ];
}